﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LGPeWJNM.DataAccessLayer
{
    /// <summary>
    /// Uma instância necessária aos algorítimos de otimização
    /// </summary>
    public class Instancia
    {
        /// <summary>
        /// Melhor valor já obtido nesta instancia de acordo com os resultados da web
        /// </summary>
        public int Otimo { get; set; }

        /// <summary>
        /// Quantidade de veículos disponibilizados para a otimização
        /// </summary>
        public int QuantidadeVeiculos { get; set; }

        /// <summary>
        /// Capacidade de cada veículo
        /// </summary>
        /// <remarks>Todos os veículos são considerados de mesma capacidade, a frota é homogênea</remarks>
        public int CapacidadeVeiculo { get; set; }

        /// <summary>
        /// Coordenadas do depósito
        /// </summary>
        public LGPeWJNM.BaseClasses.Point Deposito { get; set; }

        /// <summary>
        /// Grupo de clientes a serem atendidos
        /// </summary>
        public IEnumerable<LGPeWJNM.BaseClasses.ICliente> Destinos { get; set; }

        /// <summary>
        /// Construtor da instância a partir de um arquivo texto
        /// </summary>
        /// <example>
        /// A-n32-k5,784<br/>
        /// 5<br/>
        /// 3<br/>
        /// 100<br/>
        /// 1,82,76,0<br/>
        /// 2,96,44,19<br/>
        /// 3,50,5,21<br/>
        /// [...]
        /// </example>
        /// <remarks>
        /// O Arquivo segue o seguinte formato:<br/>
        /// nome da instância<br/>
        /// quantidade de veículos<br/>
        /// número de nós (clientes e depósito)<br/>
        /// capacidade dos veículos<br/>
        /// número de ordem,abcissa,ordenada,demanda (o depósito tem demanda 0)<br/>
        /// ...demais pontos do nó
        /// </remarks>
        /// <param name="filePath">caminho para um arquivo texto</param>
        public Instancia(string filePath)
        {
            var dir = System.IO.Path.GetDirectoryName(filePath);
            var fileName = System.IO.Path.GetFileName(filePath);

            using (var cnn = new System.Data.OleDb.OleDbConnection())
            using (var cmd = new System.Data.OleDb.OleDbCommand(string.Format("SELECT * FROM [{0}]", fileName), cnn))
            {
                cnn.ConnectionString = string.Format("Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};Extended Properties=\"text;HDR=No;\"", dir);
                cnn.Open();

                using (var dr = cmd.ExecuteReader())
                {
                    var dest = new List<LGPeWJNM.BaseClasses.Cliente>();
                    Destinos = dest;
                    int l = 0;
                    while (dr.Read())
                    {
                        if (l < 5)
                        {
                            switch (l)
                            {
                                case 0:
                                    Otimo = dr.GetInt32(1);
                                    break;
                                case 1:
                                    QuantidadeVeiculos = dr.GetInt32(0);
                                    break;
                                case 3:
                                    CapacidadeVeiculo = dr.GetInt32(0);
                                    break;
                                case 4:
                                    Deposito = new LGPeWJNM.BaseClasses.Point() { X = dr.GetInt32(1), Y = dr.GetInt32(2) };
                                    break;
                            }
                            l++;
                            continue;
                        }
                        dest.Add(new LGPeWJNM.BaseClasses.Cliente()
                        {
                            Id = dr.GetInt32(0),
                            Coordenada = new LGPeWJNM.BaseClasses.Point()
                            {
                                X = dr.GetInt32(1),
                                Y = dr.GetInt32(2)
                            },
                            Demanda = dr.GetInt32(3)
                        });
                    }
                }
            }
        }

        /// <summary>
        /// Cria uma instância pela leitura de um System.IO.StreamReader, o conteúdo é proviniente do site http://www.branchandcut.org
        /// </summary>
        /// <param name="reader">Um leitor iniciado a partir de um arquivo '.vrp'</param>
        internal Instancia(System.IO.StreamReader reader)
        {
            int dim = 0;
            while (!reader.EndOfStream)
            {
                var line = reader.ReadLine();
                var lineType = line.Split(':')[0].Trim();
                switch (lineType)
                {
                    case "COMMENT":
                        if (line.Contains("alue:"))
                            this.Otimo = int.Parse(line.Split(':').Last().Replace(")", string.Empty));
                        else
                            this.Otimo = int.MaxValue;
                        var t = line.Split(',').Where(s => s.Trim().Contains("of trucks"));
                        this.QuantidadeVeiculos = int.Parse(t.First().Split(':')[1].Replace(")", string.Empty));
                        break;
                    case "DIMENSION":
                        dim = int.Parse(line.Split(':').Last().Trim());
                        break;
                    case "EDGE_WEIGHT_TYPE":
                        if (line.Split(':').Last().Trim() != "EUC_2D")//carrega somente problemas determinados por distâncias eucledianas, caso contrário deixa a instância sem destinos definidos.
                            return;
                        break;
                    case "CAPACITY":
                        this.CapacidadeVeiculo = int.Parse(line.Split(':').Last().Trim());
                        break;
                    case "NODE_COORD_SECTION":
                        var dest = new List<LGPeWJNM.BaseClasses.Cliente>();
                        this.Destinos = dest;
                        while (true)
                        {
                            line = reader.ReadLine();
                            var sLine = line.Trim().Split(' ').Where(s => !string.IsNullOrWhiteSpace(s)).Select(s => double.Parse(s)).ToArray();
                            var cli = new LGPeWJNM.BaseClasses.Cliente()
                            {
                                Id = int.Parse(sLine[0].ToString()),
                                Coordenada = new LGPeWJNM.BaseClasses.Point()
                                {
                                    X = sLine[1],
                                    Y = sLine[2]
                                }
                            };
                            dest.Add(cli);
                            if (dim == sLine[0])
                                break;
                        }
                        break;
                    case "DEMAND_SECTION":
                        while (true)
                        {
                            line = reader.ReadLine();
                            var sLine = line.Trim().Split(' ').Select(s => int.Parse(s)).ToArray();
                            this.Destinos.ElementAt(sLine[0] - 1).Demanda = sLine[1];
                            if (dim == sLine[0])
                                break;
                        }
                        break;
                }
            }
            Deposito = new LGPeWJNM.BaseClasses.Point()
            {
                X = ((List<LGPeWJNM.BaseClasses.Cliente>)this.Destinos).First().Coordenada.X,
                Y = ((List<LGPeWJNM.BaseClasses.Cliente>)this.Destinos).First().Coordenada.Y
            };
            ((List<LGPeWJNM.BaseClasses.Cliente>)this.Destinos).RemoveAt(0);
        }

        internal Instancia() { }
    }
}
